﻿namespace EFS.RC.CC
{
	public class Platform : EFS.Common.Async.AsyncRoot
	{
		#region Locals

		// sync access
		private static object syncAccess = new object();

		// Collaboration platform
		private Microsoft.Rtc.Collaboration.CollaborationPlatform collaborationPlatform = null;

		// roles
		private EFS.RC.CC.Applications.EndPoints.Registrar endPointRegistrar = null;

		#endregion // Locals

		#region Constructor

		/// <summary>
		/// Private constructor
		/// </summary>
		private Platform()
		{
			// make endpoints registrar
			endPointRegistrar = new EFS.RC.CC.Applications.EndPoints.Registrar();

			// settings - self provisioned
			Microsoft.Rtc.Collaboration.ProvisionedApplicationPlatformSettings platformSettings =
				new Microsoft.Rtc.Collaboration.ProvisionedApplicationPlatformSettings("Contact Center", EFS.RC.CC.Config.Settings.TrustedApplicationUrn);

			// platform object			 
			collaborationPlatform = new Microsoft.Rtc.Collaboration.CollaborationPlatform(platformSettings);
		}

		#endregion

		#region Usage

		/// <summary>
		/// Our platform
		/// </summary>
		internal static EFS.RC.CC.Platform Instance { get; private set; }

		/// <summary>
		/// Get collaboration
		/// </summary>
		internal Microsoft.Rtc.Collaboration.CollaborationPlatform CollaborationPlatform
		{
			get { return collaborationPlatform; }
		}

		/// <summary>
		/// Describe this object
		/// </summary>
		/// <returns></returns>
		public override System.String Describe()
		{
			return System.String.Format("EFS.RC.CC.Platform {0}", EFS.RC.CC.Config.Settings.TrustedApplicationUrn);
		}

		/// End poits
		internal EFS.RC.CC.Applications.EndPoints.Registrar EndPointRegistrar
		{
			get { return endPointRegistrar; }
		}

		/// <summary>
		/// Asink start platform
		/// </summary>
		public static EFS.Common.Async.AsyncProcess Start()
		{
			// log
			EFS.EventLog.Log.LogInfo("AsinkStart CCC Platform", null);

			// sync access
			if (EFS.EventLog.Config.Log.FileLog.Filter.Info)
			{
				// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", this);
			}
			
			lock (syncAccess)
			{
				try
				{
					// check
					EFS.Common.Verify.Condition(null == Instance, "Platform already started");

					// new 
					Instance = new Platform();

					// log
					EFS.EventLog.Log.LogInfo("Instance Created AsinkStart CCC Platform", null);
				}
				catch (System.Exception ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, null);

					// done
					return null;
				}
			}

			EFS.EventLog.Log.LogInfo("Calling Begin Startup", null);

			// start
			return Instance.BeginCCCStartup();
		}

		/// <summary>
		/// Asink stopp platform
		/// </summary>
		public static EFS.Common.Async.AsyncProcess Stop()
		{
			// log
			EFS.EventLog.Log.LogInfo("AsinkStop CCC Platform", null);

			// sync access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", this);
			lock (syncAccess)
			{
				// check
				EFS.Common.Verify.Condition(null != Instance, "Platform not started");
			}

			// start
			return Instance.BeginShutdown();
		}

		#endregion // Instance & start-up

		#region Startup

		/// <summary>
		/// Begin startup
		/// </summary>
		private EFS.Common.Async.AsyncProcess BeginCCCStartup()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginCCCStartup", this);

			// start-up
			return BeginAsyncProcess2(CCCStartup, OnCCCStartupComplete);
		}

		/// <summary>
		/// Platform startup
		/// </summary>
		private void CCCStartup()
		{
			// log
			EFS.EventLog.Log.LogInfo("CCCStartup", this);

			try
			{
				// start events repeater
				EFS.Common.Async.Repeater.Instance.StartRepeater();

				// only text as IM
				collaborationPlatform.InstantMessagingSettings.SupportedFormats = Microsoft.Rtc.Collaboration.InstantMessagingFormat.PlainText;

				// register for events
				collaborationPlatform.RegisterForApplicationEndpointSettings(OnApplicationEndpointSettingsDiscovered);
				collaborationPlatform.ProvisioningFailed += OnCollaborationPlatformProvisioningFailed;

				// call collaboration platform start-up
				try
				{
					collaborationPlatform.BeginStartup(this.OnCollaborationPlatformStartupComplete, null);
				}
				catch (System.InvalidOperationException ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, this);
				}
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		/// <summary>
		/// Start-up complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnCCCStartupComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnCCCStartupComplete", this);

			try
			{
				// check
				ap.Throw();

				// log
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		/// <summary>
		/// Provisioning failed
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		private void OnCollaborationPlatformProvisioningFailed(object sender, Microsoft.Rtc.Collaboration.ProvisioningFailedEventArgs args)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnCollaborationPlatformProvisioningFailed", this);

			// log ex
			EFS.EventLog.Log.LogException(args.Exception, this);
		}

		/// <summary>
		/// Called when new end point discovered / not used
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		private void OnApplicationEndpointSettingsDiscovered(object sender, Microsoft.Rtc.Collaboration.ApplicationEndpointSettingsDiscoveredEventArgs args)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnApplicationEndpointSettingsDiscovered", this);

			try
			{
				// tweek
				args.ApplicationEndpointSettings.IsDefaultRoutingEndpoint = false;
				args.ApplicationEndpointSettings.AutomaticPresencePublicationEnabled = false;
				args.ApplicationEndpointSettings.Presence.PreferredServiceCapabilities = null;
				args.ApplicationEndpointSettings.UseRegistration = false;

				// add end point
				endPointRegistrar.TryStartDiscoveredEndPoint(args.ApplicationEndpointSettings);
			}
			catch (System.Exception ex)
			{
				// handle ex
				HandleExceptionSafe(ex);
			}	
		}

		/// <summary>
		/// Start-up complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnCollaborationPlatformStartupComplete(System.IAsyncResult ar)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnCollaborationPlatformStartupComplete", this);

			try
			{
				// end
				collaborationPlatform.EndStartup(ar);

				// register for TLS
				collaborationPlatform.ConnectionAuthorizationRequested += OnServerTlsConnectionManagerConnectionAuthorizationRequested;
			}
			catch (System.Exception ex)
			{
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		/// <summary>
		/// TLS
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void OnServerTlsConnectionManagerConnectionAuthorizationRequested(object sender, Microsoft.Rtc.Signaling.ConnectionAuthorizationRequestedEventArgs e)
		{
			try
			{
				// allow
				e.Allow();
			}
			catch (System.Exception ex)
			{
				// handle ex
				HandleExceptionSafe(ex);
			}	
		}

		#endregion // Startup

		#region Runtime

		/// <summary>
		/// Unhandled exception
		/// </summary>
		/// <param name="ex"></param>
		protected override void OnException(System.Exception ex)
		{
			// check
			EFS.Common.Verify.Reference(ex, "ex");

			// log
			EFS.EventLog.Log.LogException(ex, this);

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(null, "OnException", ex, this);

			// shut down
			BeginShutdown();
		}

		#endregion

		#region Shutdown

		/// <summary>
		/// Begin sutdown
		/// </summary>
		private EFS.Common.Async.AsyncProcess BeginShutdown()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginShutdown", this);

			// shutdown
			return BeginAsyncProcess2(CCCShutdown, OnCCCShutdownComplete);
		}

		/// <summary>
		/// CCC Shut down
		/// </summary>
		private void CCCShutdown()
		{
			// log
			EFS.EventLog.Log.LogInfo("CCCShutdown", this);

			// shut down all and wait for all to terminate
			System.Collections.Generic.List<EFS.Common.Async.AsyncProcess> asyncProcesses = endPointRegistrar.ShutdownAllEndPoints();
			foreach (EFS.Common.Async.AsyncProcess asyncProcess in asyncProcesses)
			{
				// wait 
				asyncProcess.WaitComplete();
			}

			// un-register for TLS
			collaborationPlatform.ConnectionAuthorizationRequested += OnServerTlsConnectionManagerConnectionAuthorizationRequested;

			// shut down platform
			System.IAsyncResult arShutDown = collaborationPlatform.BeginShutdown(OnCollabPlatformShutdownComplete, null);

			// wait for it
			collaborationPlatform.EndShutdown(arShutDown);

			// stop repeater
			EFS.Common.Async.Repeater.Instance.StopRepeater();
		}

		/// <summary>
		/// CCC shut down complete
		/// </summary>
		/// <param name="ap"></param>
		private void OnCCCShutdownComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnCCCShutdownComplete", this);
		}

		/// <summary>
		/// Shut down complete
		/// </summary>
		/// <param name="ar"></param>
		void OnCollabPlatformShutdownComplete(System.IAsyncResult ar)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnCollabPlatformShutdownComplete", this);
		}

		#endregion // Shutdown
	}
}
